home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Languguage OS 2
/
Languguage OS II Version 10-94 (Knowledge Media)(1994).ISO
/
gnu
/
m4-1_0_3.lha
/
m4-1.0.3
/
builtin.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-12-19
|
38KB
|
1,651 lines
/*
* GNU m4 -- A simple macro processor
* Copyright (C) 1989-1992 Free Software Foundation, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* Code for all builtin macros, initialisation of symbol table, and
* expansion of user defined macros.
*/
#include "m4.h"
#ifndef size_t
#include <sys/types.h>
#endif
#include "regex.h"
#define ARG(i) (argc > (i) ? TOKEN_DATA_TEXT (argv[i]) : "")
/*
* Initialisation of builtin and predefined macros. The table
* "builtin_tab" is both used for initialisation, and by the "builtin"
* builtin.
*/
#ifndef __STDC__
#define DECLARE(name) static void name ()
#else
#define DECLARE(name) static void name (struct obstack *, int, token_data **)
#endif
DECLARE (m4___file__);
DECLARE (m4___line__);
DECLARE (m4_builtin);
DECLARE (m4_changecom);
DECLARE (m4_changequote);
DECLARE (m4_debugmode);
DECLARE (m4_debugfile);
DECLARE (m4_decr);
DECLARE (m4_define);
DECLARE (m4_defn);
DECLARE (m4_divert);
DECLARE (m4_divnum);
DECLARE (m4_dnl);
DECLARE (m4_dumpdef);
DECLARE (m4_errprint);
DECLARE (m4_esyscmd);
DECLARE (m4_eval);
DECLARE (m4_format);
DECLARE (m4_ifdef);
DECLARE (m4_ifelse);
DECLARE (m4_include);
DECLARE (m4_incr);
DECLARE (m4_index);
DECLARE (m4_indir);
DECLARE (m4_len);
DECLARE (m4_m4exit);
DECLARE (m4_m4wrap);
DECLARE (m4_maketemp);
DECLARE (m4_patsubst);
DECLARE (m4_popdef);
DECLARE (m4_pushdef);
DECLARE (m4_regexp);
DECLARE (m4_shift);
DECLARE (m4_sinclude);
DECLARE (m4_substr);
DECLARE (m4_syscmd);
DECLARE (m4_sysval);
DECLARE (m4_traceoff);
DECLARE (m4_traceon);
DECLARE (m4_translit);
DECLARE (m4_undefine);
DECLARE (m4_undivert);
#undef DECLARE
static builtin const
builtin_tab[] =
{
/* name gnu args function */
{ "__file__", TRUE, FALSE, m4___file__ },
{ "__line__", TRUE, FALSE, m4___line__ },
{ "builtin", TRUE, FALSE, m4_builtin },
{ "changecom", FALSE, FALSE, m4_changecom },
{ "changequote", FALSE, FALSE, m4_changequote },
{ "debugmode", TRUE, FALSE, m4_debugmode },
{ "debugfile", TRUE, FALSE, m4_debugfile },
{ "decr", FALSE, FALSE, m4_decr },
{ "define", FALSE, TRUE, m4_define },
{ "defn", FALSE, FALSE, m4_defn },
{ "divert", FALSE, FALSE, m4_divert },
{ "divnum", FALSE, FALSE, m4_divnum },
{ "dnl", FALSE, FALSE, m4_dnl },
{ "dumpdef", FALSE, FALSE, m4_dumpdef },
{ "errprint", FALSE, FALSE, m4_errprint },
{ "esyscmd", TRUE, FALSE, m4_esyscmd },
{ "eval", FALSE, FALSE, m4_eval },
{ "format", TRUE, FALSE, m4_format },
{ "ifdef", FALSE, FALSE, m4_ifdef },
{ "ifelse", FALSE, FALSE, m4_ifelse },
{ "include", FALSE, FALSE, m4_include },
{ "incr", FALSE, FALSE, m4_incr },
{ "index", FALSE, FALSE, m4_index },
{ "indir", TRUE, FALSE, m4_indir },
{ "len", FALSE, FALSE, m4_len },
{ "m4exit", FALSE, FALSE, m4_m4exit },
{ "m4wrap", FALSE, FALSE, m4_m4wrap },
{ "maketemp", FALSE, FALSE, m4_maketemp },
{ "patsubst", TRUE, FALSE, m4_patsubst },
{ "popdef", FALSE, FALSE, m4_popdef },
{ "pushdef", FALSE, TRUE, m4_pushdef },
{ "regexp", TRUE, FALSE, m4_regexp },
{ "shift", FALSE, FALSE, m4_shift },
{ "sinclude", FALSE, FALSE, m4_sinclude },
{ "substr", FALSE, FALSE, m4_substr },
{ "syscmd", FALSE, FALSE, m4_syscmd },
{ "sysval", FALSE, FALSE, m4_sysval },
{ "traceoff", FALSE, FALSE, m4_traceoff },
{ "traceon", FALSE, FALSE, m4_traceon },
{ "translit", FALSE, FALSE, m4_translit },
{ "undefine", FALSE, FALSE, m4_undefine },
{ "undivert", FALSE, FALSE, m4_undivert },
{ 0, FALSE, FALSE, 0 },
};
static predefined const
predefined_tab[] =
{
{ "unix", "__unix__", "" },
{ NULL, "__gnu__", "" },
{ NULL, NULL, NULL },
};
/* The number of the currently active diversion. */
static int current_diversion;
/*
* Find the builtin, which lives on ADDR
*/
const builtin *
find_builtin_by_addr (builtin_func *func)
{
const builtin *bp;
for (bp = &builtin_tab[0]; bp->name != NULL; bp++)
if (bp->func == func)
return bp;
return NULL;
}
/*
* Find the builtin, which has NAME
*/
const builtin *
find_builtin_by_name (char *name)
{
const builtin *bp;
for (bp = &builtin_tab[0]; bp->name != NULL; bp++)
if (strcmp (bp->name, name) == 0)
return bp;
return NULL;
}
/*
* Install a builtin macro with name NAME, bound to the C function given
* in BP. MODE is SYMBOL_INSERT or SYMBOL_PUSHDEF. TRACED defines
* wheather NAME is to be traced.
*/
static void
define_builtin (const char *name, const builtin *bp, symbol_lookup mode,
boolean traced)
{
symbol *sym;
sym = lookup_symbol (name, mode);
SYMBOL_TYPE (sym) = TOKEN_FUNC;
SYMBOL_MACRO_ARGS (sym) = bp->groks_macro_args;
SYMBOL_FUNC (sym) = bp->func;
SYMBOL_TRACED (sym) = traced;
}
/*
* Define a predefined or user-defined macro, with name NAME, and
* expansion TEXT. MODE destinguishes between the "define" and the
* "pushdef" case. It is also used from main ().
*/
void
define_user_macro (const char *name, const char *text, symbol_lookup mode)
{
symbol *s;
s = lookup_symbol (name, mode);
if (SYMBOL_TYPE (s) == TOKEN_TEXT)
xfree (SYMBOL_TEXT (s));
SYMBOL_TYPE (s) = TOKEN_TEXT;
SYMBOL_TEXT (s) = xstrdup (text);
}
/*
* Initialise all builtin and predefined macros.
*/
void
builtin_init (void)
{
const builtin *bp;
const predefined *pp;
for (bp = &builtin_tab[0]; bp->name != NULL; bp++)
{
if (!(no_gnu_extensions && bp->gnu_extension))
define_builtin (bp->name, bp, SYMBOL_INSERT, FALSE);
}
for (pp = &predefined_tab[0]; pp->func != NULL; pp++)
{
if (no_gnu_extensions)
{
if (pp->unix_name != NULL)
define_user_macro (pp->unix_name, pp->func, SYMBOL_INSERT);
}
else
{
if (pp->gnu_name != NULL)
define_user_macro (pp->gnu_name, pp->func, SYMBOL_INSERT);
}
}
current_diversion = 0;
}
/*
* Give friendly warnings if a builtin macro is passed an inappropriate
* number of arguments. NAME is macro name for messages, ARGC is actual
* number of arguments, MIN is minimum number of acceptable arguments,
* negative if applicable, MAX is maximum number, negative if in not
* applicable.
*/
static boolean
bad_argc (token_data *name, int argc, int min, int max)
{
boolean isbad = FALSE;
if (min > 0 && argc < min)
{
if (!suppress_warnings)
warning ("too few arguments to built-in `%s'", TOKEN_DATA_TEXT (name));
isbad = TRUE;
}
else if (max > 0 && argc > max && !suppress_warnings)
warning ("excess arguments to built-in `%s' ignored",
TOKEN_DATA_TEXT (name));
return isbad;
}
/*
* The function numeric_arg () converts ARG to an int pointed to by
* VALUEP. If the conversion fails, print error message for macro
* MACRO. Return TRUE iff conversion succeeds.
*/
static boolean
numeric_arg (token_data *macro, char *arg, int *valuep)
{
if (sscanf (arg, "%d", valuep) != 1)
{
error ("non-numeric argument to built-in `%s'", TOKEN_DATA_TEXT (macro));
return FALSE;
}
return TRUE;
}
/*
* The function ntoa () converts VALUE to a signed ascii representation in
* radix RADIX.
*/
/* Digits for number to ascii conversions. */
static char const digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
static char *
ntoa (register int value, int radix)
{
boolean negative;
unsigned int uvalue;
static char str[256];
register char *s = &str[sizeof str];
*--s = '\0';
if (value < 0)
{
negative = TRUE;
uvalue = (unsigned int) -value;
}
else
{
negative = FALSE;
uvalue = (unsigned int) value;
}
do
{
*--s = digits[uvalue % radix];
uvalue /= radix;
}
while (uvalue > 0);
if (negative)
*--s = '-';
return s;
}
/*
* Format an int VAL, and stuff it into an obstack OBS. Used for macros
* expanding to numbers.
*/
static void
shipout_int (struct obstack *obs, int val)
{
char *s;
s = ntoa (val, 10);
obstack_grow (obs, s, strlen (s));
}
/*
* Print ARGC arguments from the table ARGV to obstack OBS, separated by
* SEP, and quoted by the current quotes, if QUOTED is TRUE.
*/
static void
dump_args (struct obstack *obs, int argc, token_data **argv,
char *sep, boolean quoted)
{
int i;
int len = strlen (sep);
for (i = 1; i < argc; i++)
{
if (i > 1)
obstack_grow (obs, sep, len);
if (quoted)
obstack_grow (obs, lquote, len_lquote);
obstack_grow (obs, TOKEN_DATA_TEXT (argv[i]),
strlen (TOKEN_DATA_TEXT (argv[i])));
if (quoted)
obstack_grow (obs, rquote, len_rquote);
}
}
/*
* The rest of this file is code for builtins and expansion of user
* defined macros. All the functions for builtins have a prototype as:
*
* void m4_MACRONAME (struct obstack *obs, int argc, char *argv[]);
*
* The function are expected to leave their expansion on the obstack
* OBS, as an unfinished object. ARGV is a table of ARGC pointers to
* the individual arguments to the macro. Please note that in general
* argv[argc] != NULL.
*/
/*
* The first section are macros for definining, undefining, examining,
* changing, ... other macros.
*/
/*
* The function define_macro is common for the builtins "define",
* "undefine", "pushdef" and "popdef". ARGC and ARGV is as for the
* caller, and MODE argument determines how the macro name is entered
* into the symbol table.
*/
static void
define_macro (int argc, token_data **argv, symbol_lookup mode)
{
const builtin *bp;
if (bad_argc (argv[0], argc, 3, 3))
return;
if (TOKEN_DATA_TYPE (argv[1]) != TOKEN_TEXT)
return;
switch (TOKEN_DATA_TYPE (argv[2]))
{
case TOKEN_TEXT:
define_user_macro (ARG (1), ARG (2), mode);
break;
case TOKEN_FUNC:
bp = find_builtin_by_addr (TOKEN_DATA_FUNC (argv[2]));
if (bp == NULL)
return;
else
define_builtin (ARG (1), bp, mode, TOKEN_DATA_FUNC_TRACED (argv[2]));
break;
default:
internal_error ("Bad token data type in define_macro ()");
break;
}
return;
}
static void
m4_define (struct obstack *obs, int argc, token_data **argv)
{
define_macro (argc, argv, SYMBOL_INSERT);
}
static void
m4_undefine (struct obstack *obs, int argc, token_data **argv)
{
if (bad_argc (argv[0], argc, 2, 2))
return;
lookup_symbol (ARG (1), SYMBOL_DELETE);
}
static void
m4_pushdef (struct obstack *obs, int argc, token_data **argv)
{
define_macro (argc, argv, SYMBOL_PUSHDEF);
}
static void
m4_popdef (struct obstack *obs, int argc, token_data **argv)
{
if (bad_argc (argv[0], argc, 2, 2))
return;
lookup_symbol (ARG (1), SYMBOL_POPDEF);
}
/*
* Conditionals of m4.
*/
static void
m4_ifdef (struct obstack *obs, int argc, token_data **argv)
{
symbol *s;
char *result;
if (bad_argc (argv[0], argc, 3, 4))
return;
s = lookup_symbol (ARG (1), SYMBOL_LOOKUP);
if (s != NULL)
result = ARG (2);
else if (argc == 4)
result = ARG (3);
else
result = NULL;
if (result != NULL)
obstack_grow (obs, result, strlen (result));
}
static void
m4_ifelse (struct obstack *obs, int argc, token_data **argv)
{
char *result;
token_data *argv0;
if (argc == 2)
return;
argv0 = argv[0];
argv++;
argc--;
result = NULL;
while (result == NULL)
{
if (bad_argc (argv0, argc, 3, -1))
return;
if (strcmp (ARG (0), ARG (1)) == 0)
result = ARG (2);
else
switch (argc)
{
case 3:
return;
case 4:
result = ARG (3);
break;
default:
argc -= 3;
argv += 3;
}
}
obstack_grow (obs, result, strlen (result));
}
/*
* The function dump_symbol () is for use by "dumpdef". It builds up a
* table of all defined, un-shadowed, symbols. The structure
* dump_symbol_data is used to pass the information needed from call to
* call to dump_symbol.
*/
struct dump_symbol_data
{
struct obstack *obs; /* obstack for table */
symbol **base; /* base of table */
int size; /* size of table */
};
static void
dump_symbol (symbol *sym, struct dump_symbol_data *data)
{
if (!SYMBOL_SHADOWED (sym) && SYMBOL_TYPE (sym) != TOKEN_VOID)
{
obstack_blank (data->obs, sizeof (symbol *));
data->base = (symbol **) obstack_base (data->obs);
data->base[data->size++] = sym;
}
}
/*
* qsort comparison routine, for sorting the table made in m4_dumpdef ().
*/
static int
dumpdef_cmp (const void *s1, const void *s2)
{
return strcmp (SYMBOL_NAME (* (const symbol **) s1),
SYMBOL_NAME (* (const symbol **) s2));
}
/*
* Implementation of "dumpdef" itself. It builds up a table of pointers
* to symbols, sorts it and prints the sorted table.
*/
static void
m4_dumpdef (struct obstack *obs, int argc, token_data **argv)
{
symbol *s;
int i;
struct dump_symbol_data data;
const builtin *bp;
data.obs = obs;
data.base = (symbol **) obstack_base (obs);
data.size = 0;
if (argc == 1)
{
hack_all_symbols (dump_symbol, (char *) &data);
}
else
{
for (i = 1; i < argc; i++)
{
s = lookup_symbol (TOKEN_DATA_TEXT (argv[i]), SYMBOL_LOOKUP);
if (s != NULL && SYMBOL_TYPE (s) != TOKEN_VOID)
dump_symbol (s, &data);
else
error ("Undefined name %s", TOKEN_DATA_TEXT (argv[i]));
}
}
/* make table of symbols invisible to expand_macro () */
(void) obstack_finish (obs);
qsort ((char *) data.base, data.size, sizeof (symbol *), dumpdef_cmp);
for (; data.size > 0; --data.size, data.base++)
{
debug_print ("%s:\t", SYMBOL_NAME (data.base[0]));
switch (SYMBOL_TYPE (data.base[0]))
{
case TOKEN_TEXT:
if (debug_level & DEBUG_TRACE_QUOTE)
debug_print ("%s%s%s\n", lquote, SYMBOL_TEXT (data.base[0]), rquote);
else
debug_print ("%s\n", SYMBOL_TEXT (data.base[0]));
break;
case TOKEN_FUNC:
bp = find_builtin_by_addr (SYMBOL_FUNC (data.base[0]));
if (bp == NULL)
internal_error ("built-in not found in builtin table!");
debug_print ("<%s>\n", bp->name);
break;
default:
internal_error ("Bad token data type in m4_dumpdef ()");
break;
}
}
}
/*
* This is GNU specific. The builtin "builtin" allows calls to builtin
* macros, even if their definition has been overridden or shadowed. It
* is thus possible to redefine builtins, and still access their
* original definition.
*
* This macro is not available in compatibility mode.
*/
static void
m4_builtin (struct obstack *obs, int argc, token_data **argv)
{
const builtin *bp;
char *name = ARG (1);
if (bad_argc (argv[0], argc, 2, -1))
return;
bp = find_builtin_by_name (name);
if (bp == NULL)
error ("Undefined name %s", name);
else
(*bp->func) (obs, argc - 1, argv + 1);
}
/*
* This is GNU specific. The builtin "indir" allows indirect calls to
* macros, even if their name is not a proper macro name. It
* is thus possible to define macros with ill-formed names for internal
* use in larger macro packages.
*
* This macro is not available in compatibility mode.
*/
static void
m4_indir (struct obstack *obs, int argc, token_data **argv)
{
symbol *s;
char *name = ARG (1);
if (bad_argc (argv[0], argc, 1, -1))
return;
s = lookup_symbol (name, SYMBOL_LOOKUP);
if (s == NULL)
error ("Undefined macro `%s'", name);
else
call_macro (s, argc - 1, argv + 1, obs);
}
/*
* The macro "defn" returns the quoted definition of the macro named by
* the first argument. If the macro is builtin, it will push a special
* macro-definition token on ht input stack.
*/
static void
m4_defn (struct obstack *obs, int argc, token_data **argv)
{
symbol *s;
if (bad_argc (argv[0], argc, 2, 2))
return;
s = lookup_symbol (ARG (1), SYMBOL_LOOKUP);
if (s == NULL)
return;
switch (SYMBOL_TYPE (s))
{
case TOKEN_TEXT:
obstack_grow (obs, lquote, len_lquote);
obstack_grow (obs, SYMBOL_TEXT (s), strlen (SYMBOL_TEXT (s)));
obstack_grow (obs, rquote, len_rquote);
break;
case TOKEN_FUNC:
push_macro (SYMBOL_FUNC (s), SYMBOL_TRACED (s));
break;
case TOKEN_VOID:
break;
default:
internal_error ("Bad symbol type in m4_defn ()");
break;
}
}
/*
* This section contains macros to handle the builtins "syscmd",
* "esyscmd" and "sysval". "esyscmd" is GNU specific.
*/
/* Exit code from last "syscmd" command. */
static int sysval;
static void
m4_syscmd (struct obstack *obs, int argc, token_data **argv)
{
if (bad_argc (argv[0], argc, 2, 2))
return;
sysval = system (ARG (1));
}
static void
m4_esyscmd (struct obstack *obs, int argc, token_data **argv)
{
FILE *pin;
int ch;
if (bad_argc (argv[0], argc, 2, 2))
return;
pin = popen (ARG (1), "r");
if (pin == NULL)
{
error ("Cannot open pipe to command \"%s\": %s", ARG (1), syserr ());
sysval = 0xff << 8;
}
else
{
while ((ch = getc (pin)) != EOF)
obstack_1grow (obs, (char) ch);
sysval = pclose (pin);
}
}
static void
m4_sysval (struct obstack *obs, int argc, token_data **argv)
{
shipout_int (obs, (sysval >> 8) & 0xff);
}
/*
* This section contains the top level code for the "eval" builtin. The
* actual work is done in the function evaluate (), which lives in
* eval.c.
*/
static void
m4_eval (struct obstack *obs, int argc, token_data **argv)
{
int value;
int radix = 10;
int min = 1;
char *s;
if (bad_argc (argv[0], argc, 2, 4))
return;
if (argc >= 3 && !numeric_arg (argv[0], ARG (2), &radix))
return;
if (radix <= 1 || radix > strlen (digits))
{
error ("radix in eval out of range (radix = %d)", radix);
return;
}
if (argc >= 4 && !numeric_arg (argv[0], ARG (3), &min))
return;
if (min <= 0)
{
error ("negative width to eval");
return;
}
if (evaluate (ARG (1), &value))
return;
s = ntoa (value, radix);
if (*s == '-')
{
obstack_1grow (obs, '-');
min--;
s++;
}
for (min -= strlen (s); --min >= 0;)
obstack_1grow (obs, '0');
obstack_grow (obs, s, strlen (s));
}
static void
m4_incr (struct obstack *obs, int argc, token_data **argv)
{
int value;
if (bad_argc (argv[0], argc, 2, 2))
return;
if (!numeric_arg (argv[0], ARG (1), &value))
return;
shipout_int (obs, value + 1);
}
static void
m4_decr (struct obstack *obs, int argc, token_data **argv)
{
int value;
if (bad_argc (argv[0], argc, 2, 2))
return;
if (!numeric_arg (argv[0], ARG (1), &value))
return;
shipout_int (obs, value - 1);
}
/*
* This section contains the macros "divert", "undivert" and "divnum"
* for handling diversion. The utility functions used lives in output.c
*/
/*
* Divert further output to the diversion given by ARGV[1]. Out of
* range means discard further output.
*/
static void
m4_divert (struct obstack *obs, int argc, token_data **argv)
{
int i = 0;
if (bad_argc (argv[0], argc, 1, 2))
return;
if (argc == 2 && !numeric_arg (argv[0], ARG (1), &i))
return;
make_diversion (i);
current_diversion = i;
}
/*
* Expand to the current diversion number, -1 if none.
*/
static void
m4_divnum (struct obstack *obs, int argc, token_data **argv)
{
if (bad_argc (argv[0], argc, 1, 1))
return;
shipout_int (obs, current_diversion);
}
/*
* Bring back the diversion given by the argument list. If none is
* specified, bring back all diversions. GNU specific is the option of
* undiverting named files, by passing a non-numeric argument to
* undivert ().
*/
static void
m4_undivert (struct obstack *obs, int argc, token_data **argv)
{
int i, div;
FILE *fp;
if (argc == 1)
{
undivert_all ();
}
else
for (i = 1; i < argc; i++)
{
if (sscanf (ARG (i), "%d", &div) == 1)
insert_diversion (div);
else if (no_gnu_extensions)
error ("non-numeric argument to %s", TOKEN_DATA_TEXT (argv[0]));
else
{
fp = path_search (ARG (i));
if (fp != NULL)
{
insert_file (fp);
fclose (fp);
}
else
error ("can't undivert %s: %s", ARG (i), syserr ());
}
}
}
/*
* This section contains various macros, which does not fall into any
* specific group. These are "dnl", "shift", "changequote" and
* "changecom".
*/
/*
* Delete all subsequent whitespace from input. The function
* skip_line () lives in input.c.
*/
static void
m4_dnl (struct obstack *obs, int argc, token_data **argv)
{
if (bad_argc (argv[0], argc, 1, 1))
return;
skip_line ();
}
/*
* Shift all argument one to the left, discarding the first argument.
* Each output argument is quoted with the current quotes.
*/
static void
m4_shift (struct obstack *obs, int argc, token_data **argv)
{
dump_args (obs, argc - 1, argv + 1, ",", TRUE);
}
/*
* Change the current quotes. The function set_quotes () lives in
* input.c
*/
static void
m4_changequote (struct obstack *obs, int argc, token_data **argv)
{
if (bad_argc (argv[0], argc, 1, 3))
return;
set_quotes ((argc >= 2) ? TOKEN_DATA_TEXT (argv[1]) : NULL,
(argc >= 3) ? TOKEN_DATA_TEXT (argv[2]) : NULL);
}
/*
* Change the current comment delimiters. The function set_comment ()
* lives in input.c
*/
static void
m4_changecom (struct obstack *obs, int argc, token_data **argv)
{
if (bad_argc (argv[0], argc, 1, 3))
return;
if (argc == 1)
set_comment ("", ""); /* disable comments */
else
set_comment (TOKEN_DATA_TEXT (argv[1]),
(argc >= 3) ? TOKEN_DATA_TEXT (argv[2]) : NULL);
}
/*
* This section contains macros for inclusion of other files --
* "include" and "sinclude". This differs from bringing back
* diversions, in that the input is scanned before being copied to the
* output.
*/
/*
* Generic include function. Include the file given by the first
* argument, if it exists. Complain about inaccesible files iff
* SILENT is FALSE.
*/
static void
include (int argc, token_data **argv, boolean silent)
{
FILE *fp;
if (bad_argc (argv[0], argc, 2, 2))
return;
fp = path_search (ARG (1));
if (fp == NULL)
{
if (!silent)
error ("can't open %s: %s", ARG (1), syserr ());
return;
}
push_file (fp, ARG (1));
}
/*
* Include a file, complaining in case of errors.
*/
static void
m4_include (struct obstack *obs, int argc, token_data **argv)
{
include (argc, argv, FALSE);
}
/*
* Include a file, ignoring errors.
*/
static void
m4_sinclude (struct obstack *obs, int argc, token_data **argv)
{
include (argc, argv, TRUE);
}
/*
* More miscellaneous builtins -- "maketemp", "errprint", "__file__" and
* "__line__". The last two are GNU specific.
*/
/*
* Use the first argument as at template for a temporary file name.
*/
static void
m4_maketemp (struct obstack *obs, int argc, token_data **argv)
{
if (bad_argc (argv[0], argc, 2, 2))
return;
mktemp (ARG (1));
obstack_grow (obs, ARG (1), strlen (ARG (1)));
}
/*
* Print all arguments on standard error.
*/
static void
m4_errprint (struct obstack *obs, int argc, token_data **argv)
{
dump_args (obs, argc, argv, " ", FALSE);
obstack_1grow (obs, '\0');
fprintf (stderr, "%s", (char *) obstack_finish (obs));
fflush (stderr);
}
static void
m4___file__ (struct obstack *obs, int argc, token_data **argv)
{
if (bad_argc (argv[0], argc, 1, 1))
return;
obstack_grow (obs, lquote, len_lquote);
obstack_grow (obs, current_file, strlen (current_file));
obstack_grow (obs, rquote, len_rquote);
}
static void
m4___line__ (struct obstack *obs, int argc, token_data **argv)
{
if (bad_argc (argv[0], argc, 1, 1))
return;
shipout_int (obs, current_line);
}
/*
* This section contains various macros for exiting, saving input until
* EOF is seen, and tracing macro calls. That is: "m4exit", "m4wrap",
* "traceon" and "traceoff".
*/
/*
* Exit immediately, with exitcode specified by the first argument, 0 if
* no arguments are present.
*/
static void
m4_m4exit (struct obstack *obs, int argc, token_data **argv)
{
int exit_code = 0;
if (bad_argc (argv[0], argc, 1, 2))
return;
if (argc == 2 && !numeric_arg (argv[0], ARG (1), &exit_code))
exit_code = 0;
exit (exit_code);
}
/*
* Save the argument text until EOF has been seen, allowing for user
* specified cleanup action. GNU version saves all arguments, the
* standard version only the first.
*/
static void
m4_m4wrap (struct obstack *obs, int argc, token_data **argv)
{
if (no_gnu_extensions)
{
obstack_grow (obs, ARG (1), strlen (ARG (1)));
}
else
dump_args (obs, argc, argv, " ", FALSE);
obstack_1grow (obs, '\0');
push_wrapup (obstack_finish (obs));
}
/*
* Enable tracing of all specified macros, or all, if none is specified.
* Tracing is disabled by default, when a macro is defined. This can be
* overridden by the "t" debug flag.
*/
/*
* Set_trace () is used by "traceon" and "traceoff" to enable and disable
* tracing of a macro. It disables tracing if DATA is NULL, otherwise it
* enable tracing.
*/
static void
set_trace (symbol *sym, char *data)
{
SYMBOL_TRACED (sym) = (boolean) (data != NULL);
}
static void
m4_traceon (struct obstack *obs, int argc, token_data **argv)
{
symbol *s;
int i;
if (argc == 1)
hack_all_symbols (set_trace, (char *) obs);
else
for (i = 1; i < argc; i++)
{
s = lookup_symbol (TOKEN_DATA_TEXT (argv[i]), SYMBOL_LOOKUP);
if (s != NULL)
set_trace (s, (char *) obs);
else
error ("Undefined name %s", TOKEN_DATA_TEXT (argv[i]));
}
}
/*
* Disable tracing of all specified macros, or all, if none is
* specified.
*/
static void
m4_traceoff (struct obstack *obs, int argc, token_data **argv)
{
symbol *s;
int i;
if (argc == 1)
hack_all_symbols (set_trace, NULL);
else
for (i = 1; i < argc; i++)
{
s = lookup_symbol (TOKEN_DATA_TEXT (argv[i]), SYMBOL_LOOKUP);
if (s != NULL)
set_trace (s, NULL);
else
error ("Undefined name %s", TOKEN_DATA_TEXT (argv[i]));
}
}
/*
* On-the-fly control of the format of the tracing output. It takes one
* argument, which is a character string like given to the -d option, or
* none in which case the debug_level is zeroed.
*/
static void
m4_debugmode (struct obstack *obs, int argc, token_data **argv)
{
int new_debug_level;
int change_flag;
if (bad_argc (argv[0], argc, 1, 2))
return;
if (argc == 1)
debug_level = 0;
else
{
if (ARG (1)[0] == '+' || ARG (1)[0] == '-')
{
change_flag = ARG (1)[0];
new_debug_level = debug_decode (ARG (1) + 1);
}
else
{
change_flag = 0;
new_debug_level = debug_decode (ARG (1));
}
if (new_debug_level < 0)
error ("debugmode: bad debug flags: `%s'", ARG (1));
else
{
switch (change_flag)
{
case 0:
debug_level = new_debug_level;
break;
case '+':
debug_level |= new_debug_level;
break;
case '-':
debug_level &= ~new_debug_level;
break;
}
}
}
}
/*
* Specify the destination of the debugging output. With one argument,
* the argument is taken as a file name, with no arguments, revert to
* stderr.
*/
static void
m4_debugfile (struct obstack *obs, int argc, token_data **argv)
{
if (bad_argc (argv[0], argc, 1, 2))
return;
if (argc == 1)
debug_set_output (NULL);
else if (!debug_set_output (ARG (1)))
error ("cannot set error file: %s: %s", ARG (1), syserr ());
}
/*
* This section contains text processing macros: "len", "index",
* "substr", "translit", "format", "regexp" and "patsubst". The last
* three are GNU specific.
*/
/*
* Expand to the length of the first argument.
*/
static void
m4_len (struct obstack *obs, int argc, token_data **argv)
{
if (bad_argc (argv[0], argc, 2, 2))
return;
shipout_int (obs, strlen (ARG (1)));
}
/*
* The macro expands to the first index of the second argument in the
* first argument.
*/
static void
m4_index (struct obstack *obs, int argc, token_data **argv)
{
char *cp, *last;
int l1, l2, retval;
if (bad_argc (argv[0], argc, 3, 3))
return;
l1 = strlen (ARG (1));
l2 = strlen (ARG (2));
last = ARG (1) + l1 - l2;
for (cp = ARG (1); cp <= last; cp++)
{
if (strncmp (cp, ARG (2), l2) == 0)
break;
}
retval = (cp <= last) ? cp - ARG (1) : -1;
shipout_int (obs, retval);
}
/*
* The macro "substr" extracts substrings from the first argument,
* starting from the index given by the second argument, extending for a
* length given by the third argument. If the third argument is
* missing, the substring extends to the end of the first argument.
*/
static void
m4_substr (struct obstack *obs, int argc, token_data **argv)
{
int start, length, avail;
if (bad_argc (argv[0], argc, 3, 4))
return;
length = avail = strlen (ARG (1));
if (!numeric_arg (argv[0], ARG (2), &start))
return;
if (argc == 4 && !numeric_arg (argv[0], ARG (3), &length))
return;
if (start < 0 || length <= 0 || start >= avail)
return;
if (start + length > avail)
length = avail - start;
obstack_grow (obs, ARG (1) + start, length);
}
/*
* The macro "translit" translates all characters in the first
* argument, which are present in the second argument, into the
* corresponding character from the third argument. If the third
* argument is shorter than the second, the extra characters in the
* second argument, are delete from the first (pueh).
*
* Ranges are allowed in the second and third argument. They are
* expanded in the following function, and the expanded strings, without
* any ranges left, are used to translate the characters of the first
* argument. A single - (dash) can be included in the strings by being
* the first or the last character in the string. If the first
* character in a range is after the first in the character set, the
* range is made backwards, thus 9-0 is the string 9876543210.
*/
static char *
expand_ranges (char *s, struct obstack *obs)
{
char from;
char to;
for (from = '\0'; *s != '\0'; from = *s++)
{
if (*s == '-' && from != '\0')
{
to = *++s;
if (to == '\0')
obstack_1grow (obs, '-'); /* trailing dash */
else if (from <= to)
{
while (from++ < to)
obstack_1grow (obs, from);
}
else
{
while (--from >= to)
obstack_1grow (obs, from);
}
}
else
obstack_1grow (obs, *s);
}
obstack_1grow (obs, '\0');
return obstack_finish (obs);
}
static void
m4_translit (struct obstack *obs, int argc, token_data **argv)
{
register char *data, *tmp;
char *from, *to;
int tolen;
if (bad_argc (argv[0], argc, 3, 4))
return;
from = ARG (2);
if (index (from, '-') != NULL)
{
from = expand_ranges (from, obs);
if (from == NULL)
return;
}
if (argc == 4)
{
to = ARG (3);
if (index (to, '-') != NULL)
{
to = expand_ranges (to, obs);
if (to == NULL)
return;
}
}
else
to = "";
tolen = strlen (to);
for (data = ARG (1); *data; data++)
{
tmp = (char *) index (from, *data);
if (tmp == NULL)
{
obstack_1grow (obs, *data);
}
else
{
if (tmp - from < tolen)
obstack_1grow (obs, *(to + (tmp - from)));
}
}
}
/*
* Frontend for printf like formatting. The function format () lives in
* the file format.c.
*/
static void
m4_format (struct obstack *obs, int argc, token_data **argv)
{
format (obs, argc - 1, argv + 1);
}
/*
* Function to perform substitution by regular expressions. Used by the
* builtins regexp and patsubst. The changed text is place on the
* obstack. The substitution is REPL, with \& substituted by VICTIM (of
* length LENGTH), and \N substituted by the text matched by the Nth
* parenthesized sub-expression, taken from REGS.
*/
static void
substitute (struct obstack *obs, char *victim, int length,
char *repl, struct re_registers *regs)
{
register unsigned int ch;
for (;;)
{
while ((ch = *repl++) != '\\')
{
if (ch == '\0')
return;
obstack_1grow (obs, ch);
}
switch ((ch = *repl++))
{
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
ch -= '0';
if (regs->end[ch] > 0)
obstack_grow (obs, victim + regs->start[ch],
regs->end[ch] - regs->start[ch]);
break;
case '&':
obstack_grow (obs, victim, length);
break;
default:
obstack_1grow (obs, ch);
break;
}
}
}
/*
* Regular expression version of index. Given two arguments, expand to
* the index of the first match of the second argument (a regexp) in the
* first. Expand to -1 if here is no match. Given a third argument, is
* changes the expansion to this argument,
*/
static void
m4_regexp (struct obstack *obs, int argc, token_data **argv)
{
char *victim; /* first argument */
char *regexp; /* regular expression */
char *repl; /* replacement string */
struct re_pattern_buffer buf; /* compiled regular expression */
struct re_registers regs; /* for subexpression matches */
const char *msg; /* error message from re_compile_pattern */
int startpos; /* start position of match */
int length; /* length of first argument */
if (bad_argc (argv[0], argc, 3, 4))
return;
victim = TOKEN_DATA_TEXT (argv[1]);
regexp = TOKEN_DATA_TEXT (argv[2]);
if (argc == 4)
repl = TOKEN_DATA_TEXT (argv[3]);
buf.buffer = NULL;
buf.allocated = 0;
buf.fastmap = NULL;
buf.translate = NULL;
msg = re_compile_pattern (regexp, strlen (regexp), &buf);
if (msg != NULL)
{
error ("bad regular expression: \"%s\": %s", regexp, msg);
return;
}
length = strlen (victim);
startpos = re_search (&buf, victim, length, 0, length, ®s);
xfree (buf.buffer);
if (startpos == -2)
{
error ("error matching regular expression \"%s\"", regexp);
return;
}
if (argc == 3)
shipout_int (obs, startpos);
else if (startpos >= 0)
substitute (obs, victim, length, repl, ®s);
return;
}
/*
* Substitute all matches of a regexp occuring in a string. Each match
* of the second argument (a regexp) in the first argument is changed to
* the third argument, with \& substituted by the matched text, and \N
* substituted by the text matched by the Nth parenthesized
* sub-expression.
*/
static void
m4_patsubst (struct obstack *obs, int argc, token_data **argv)
{
char *victim; /* first argument */
char *regexp; /* regular expression */
struct re_pattern_buffer buf; /* compiled regular expression */
struct re_registers regs; /* for subexpression matches */
const char *msg; /* error message from re_compile_pattern */
int matchpos; /* start position of match */
int offset; /* current match offset */
int length; /* length of first argument */
if (bad_argc (argv[0], argc, 3, 4))
return;
regexp = TOKEN_DATA_TEXT (argv[2]);
buf.buffer = NULL;
buf.allocated = 0;
buf.fastmap = NULL;
buf.translate = NULL;
msg = re_compile_pattern (regexp, strlen (regexp), &buf);
if (msg != NULL)
{
error ("bad regular expression: \"%s\": %s", regexp, msg);
if (buf.buffer != NULL)
xfree (buf.buffer);
return;
}
victim = TOKEN_DATA_TEXT (argv[1]);
length = strlen (victim);
offset = 0;
matchpos = 0;
while (offset < length)
{
matchpos = re_search (&buf, victim, length,
offset, length - offset, ®s);
if (matchpos < 0)
{
/*
* Match failed -- either error or there is no match in the
* rest of the string, in which case the rest of the string
* is copied verbatim.
*/
if (matchpos == -2)
error ("error matching regular expression \"%s\"", regexp);
else if (offset < length)
obstack_grow (obs, victim + offset, length - offset);
break;
}
/*
* Copy the part of the string that was skipped by re_search ().
*/
if (matchpos > offset)
obstack_grow (obs, victim + offset, matchpos - offset);
/*
* Handle the part of the string that was covered by the match.
*/
substitute (obs, victim, length, ARG (3), ®s);
/*
* Update the offset to the end of the match. If the regexp
* matched a null string, advance offset one more, to avoid
* infinite loops.
*/
offset = regs.end[0];
if (regs.start[0] == regs.end[0])
obstack_1grow (obs, victim[offset++]);
}
obstack_1grow (obs, '\0');
xfree (buf.buffer);
return;
}
/*
* This function handles all expansion of user defined and predefined
* macros. It is called with an obstack OBS, where the macros expansion
* will be placed, as an unfinished object. SYM points to the macro
* definition, giving the expansion text. ARGC and ARGV are the
* arguments, as usual.
*/
void
expand_user_macro (struct obstack *obs, symbol *sym,
int argc, token_data **argv)
{
register char *text;
int i;
for (text = SYMBOL_TEXT (sym); *text != '\0';)
{
if (*text != '$')
{
obstack_1grow (obs, *text);
text++;
continue;
}
text++;
switch (*text)
{
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
if (no_gnu_extensions)
{
i = *text++ - '0';
}
else
{
for (i = 0; isdigit (*text); text++)
i = i*10 + (*text - '0');
}
if (i < argc)
obstack_grow (obs, TOKEN_DATA_TEXT (argv[i]),
strlen (TOKEN_DATA_TEXT (argv[i])));
break;
case '#': /* number of arguments */
shipout_int (obs, argc - 1);
text++;
break;
case '*': /* all arguments */
case '@': /* ... same, but quoted */
dump_args (obs, argc, argv, ",", *text == '@');
text++;
break;
default:
obstack_1grow (obs, '$');
break;
}
}
}